﻿#define _CRT_SECURE_NO_WARNINGS

//顺序表经典OJ题2：
//88合并两个升序数组为一个升序数组(Ace11)

//给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2，另有两个整数 m 和 n ，分别表示 nums1 和 nums2 中的元素数目。
//请你 合并 nums2 到 nums1 中，使合并后的数组同样按 非递减顺序 排列。
//注意：最终，合并后数组不应由函数返回，而是存储在数组 nums1 中。为了应对这种情况，nums1 的初始长度为 m + n，其中前 m 个元素表示应合并的元素，后 n 个元素为 0 ，应忽略。nums2 的长度为 n 。
//
//示例 1：
//输入：nums1 = [1, 2, 3, 0, 0, 0], m = 3, nums2 = [2, 5, 6], n = 3
//输出：[1, 2, 2, 3, 5, 6]
//解释：需要合并[1, 2, 3] 和[2, 5, 6] 。
//合并结果是[1, 2, 2, 3, 5, 6] ，其中斜体加粗标注的为 nums1 中的元素。
//示例 2：
//输入：nums1 = [1], m = 1, nums2 = [], n = 0
//输出：[1]
//解释：需要合并[1] 和[] 。
//合并结果是[1] 。
//示例 3：
//输入：nums1 = [0], m = 0, nums2 = [1], n = 1
//输出：[1]
//解释：需要合并的数组是[] 和[1].
//合并结果是[1] 。
//注意，因为 m = 0 ，所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
//
//提示：
//nums1.length == m + n
//nums2.length == n
//0 <= m, n <= 200
//1 <= m + n <= 200
//- 109 <= nums1[i], nums2[j] <= 109
//
//进阶：你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗
#include <stdio.h>

//思路1：直接合并，再排升序(太简单，不推荐)

//复杂度分析
//时间复杂度：O((m + n)log⁡(m + n)。
//排序序列长度为 m + nm + nm + n，套用快速排序的时间复杂度即可，平均情况为 O((m + n)log⁡(m + n))O((m + n)\log(m + n))O((m + n)log(m + n))。
//空间复杂度：O(log⁡(m + n))。
//排序序列长度为 m + nm + nm + n，套用快速排序的空间复杂度即可，平均情况为 O(log⁡(m + n))O(\log(m + n))O(log(m + n))。
int cmp(int* a, int* b) {
    return *a - *b;
}
void merge1(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    for (int i = 0; i != n; ++i) {
        nums1[m + i] = nums2[i];
    }
    qsort(nums1, nums1Size, sizeof(int), cmp);
}

//思路2：顺向双指针——同位置元素比较——合并的同时让其排好序，合并完就排完

//复杂度分析
//时间复杂度：O(m + n)。
//指针移动单调递增，最多移动 m + nm + nm + n 次，因此时间复杂度为 O(m + n)O(m + n)O(m + n)。
//空间复杂度：O(m + n)。
//需要建立长度为 m + n的中间数组 sorted。
//void merge2(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
//    int p1 = 0, p2 = 0;
//    int sorted[m + n];//error
//    int cur;
//    while (p1 < m || p2 < n) {
//        if (p1 == m) {
//            cur = nums2[p2++];
//        }
//        else if (p2 == n) {
//            cur = nums1[p1++];
//        }
//        else if (nums1[p1] < nums2[p2]) {
//            cur = nums1[p1++];
//        }
//        else {
//            cur = nums2[p2++];
//        }
//        sorted[p1 + p2 - 1] = cur;
//    }
//    for (int i = 0; i != m + n; ++i) {
//        nums1[i] = sorted[i];
//    }
//}

//思路3：逆向双指针——同位置元素比较——合并的同时让其排好序，合并完就排完

//思路二中，之所以要使用临时变量，是因为如果直接合并到数组 nums1​中，nums1​中的元素可能会在取出之前被覆盖。那么如何直接避免覆盖 nums1中的元素呢？
//观察可知，nums1的后半部分是空的，可以直接覆盖而不会影响结果。因此可以指针设置为从后向前遍历，每次取两者之中的较大者放进 nums1的最后面。
//严格来说，在此遍历过程中的任意一个时刻，nums1数组中有 m−p1−1 个元素被放入 nums1的后半部，nums2数组中有 n−p2−1个元素被放入 nums1的后半部，而在指针 p1的后面，
//nums1数组有 m + n−p1−1个位置。由于
//m + n−p1−1≥m−p1−1 + n−p2−1​
//等价于
//p2≥−1
//永远成立，因此 p1​后面的位置永远足够容纳被插入的元素，不会产生 p1的元素被覆盖的情况。
//复杂度分析
//时间复杂度：O(m + n)。
//指针移动单调递减，最多移动 m + nm + nm + n 次，因此时间复杂度为 O(m + n)O(m + n)O(m + n)。
//空间复杂度：O(1)。
//直接对数组 nums1​原地修改，不需要额外空间。

//void merge3(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
//{
//    int l1 = m - 1;
//    int l2 = n - 1; int l3 = m + n - 1;
//    while (l1 >= 0 && l2 >= 0)
//    {
//        //从后往前比大小
//        if (nums1[11] > nums2[12])
//        {
//            nums1[l3--] = nums1[l1--];
//        }
//        else
//        {
//            nums1[l3--] = nums2[l2--];
//        }
//    }
//    //要么是11 < 0, 要么是12 < 0
//    while (12 >= 0)
//    {
//        nums1[l3--] = nums2[l2--];
//    }
//}
void merge3(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    //p1从后往前遍历nums1的有效数据
    //p2从后往前遍历nums2的有效数据
    //tail为合并数组存储数据的位置
    int p1 = m - 1, p2 = n - 1;
    int tail = m + n - 1;
    int cur;
    while (p1 >= 0 || p2 >= 0) {
        //nums1为空
        if (p1 == -1) {
            cur = nums2[p2--];
        }
        //nums2为空
        else if (p2 == -1) {
            cur = nums1[p1--];
        }
        else if (nums1[p1] > nums2[p2]) {
            cur = nums1[p1--];
        }
        else {
            cur = nums2[p2--];
        }
        nums1[tail--] = cur;
    }
}


void test2()
{
    int nums1[] = { 1, 2, 3, 0, 0, 0 }, m = 3, nums2[] = { 2, 5, 6 }, n = 3;
    merge3(nums1, 6, m, nums2, 3, 3);
    int i = 0;
    for (i = 0; i < 6; i++)
    {
        printf("%d ", nums1[i]);
    }
}
int main()
{
    test2();
    return 0;
}



